#include "console-common.h"

#define INT32_FUNC(x) __luacon_int32_##x
#define STATICFN(x, L) static int INT32_FUNC(x) (lua_State * L)

#ifdef _MSC_VER
int __popcount (unsigned x) {
   x = x - ((x >> 1) & 0x55555555);
   x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
   x = (x + (x >> 4)) & 0x0F0F0F0F;
   return (x * 0x01010101) >> 24;
}

int __parity(unsigned x) {
   x = x ^ (x >> 1);
   x = (x ^ (x >> 2)) & 0x11111111;
   x = x * 0x11111111;
   return (x >> 28) & 1;
}

#	define __builtin_ffs(x) (x ? msvc_ctz(x) + 1 : 0)
#	define __builtin_popcount __popcount
#	define __builtin_parity __parity
#endif
#define MULTOPR(L, x, op) int32_t x = lua_tointeger(L, 1); for (int _i = lua_gettop(L); _i > 1; _i--) { x op lua_tointeger(L, _i); }

STATICFN (imul, L)
{
    MULTOPR(L, n, *=) return lua_pushinteger(L, n), 1;
}

#if defined(__GNUC__) && defined(X86)
#define MULHI_FUNC(x) STATICFN (x##h, L) {int32_t x = lua_tointeger(L, 1), y = lua_tointeger(L, 2), z; \
	__asm__ __volatile__ (#x "l %2" : "+a"(x), "=d"(z) : "r"(y)); return lua_pushinteger(L, z), 1;}

MULHI_FUNC(mul)
MULHI_FUNC(imul)
#endif

#define IDIV_FUNC(name, op) STATICFN(name, L){int32_t x=lua_tointeger(L,1),y=lua_tointeger(L,2); \
	if (y==0)luaL_error(L,"attempt to divide by zero");lua_pushinteger(L, x op y); return 1;}
#define SIMP_FUNC(name, expr) STATICFN(name, L){int32_t x=lua_tointeger(L, 1); return lua_pushinteger(L, expr), 1;}
#define BITW_FUNC(name, fn1, fn2) STATICFN(name, L){MULTOPR(L, x, &=); return lua_push##fn1(L, __builtin_##fn2(x)), 1;}

IDIV_FUNC(idiv, /)
IDIV_FUNC(imod, %)
SIMP_FUNC(ffs, __builtin_ffs(x))
SIMP_FUNC(clz, x?__builtin_clz(x):32)
SIMP_FUNC(ctz, x?__builtin_ctz(x):32)
BITW_FUNC(popcnt, integer, popcount)
BITW_FUNC(parity, boolean, parity)

void luaopen_con_int32_api_ (lua_State * L)
{
#define ARGEXT0(x) {#x,INT32_FUNC(x)},
	const static struct luaL_Reg table1 [] = {
		ARGEXT0(imul) ARGEXT0(idiv) ARGEXT0(imod) ARGEXT0(ffs) ARGEXT0(clz) ARGEXT0(ctz) ARGEXT0(popcnt) ARGEXT0(parity)
#if defined(__GNUC__) && defined(X86)
		ARGEXT0(mulh) ARGEXT0(imulh)
#endif
		{NULL, NULL}
	};
#undef ARGEXT0
	lua_newtable(L);
	luaL_set_cfuncs(L, table1);
}